/*
 * @ClassName request.dart
 * @Description 网络请求
 * @Author SuPeiSen
 * @Date 2020/7/13 17:28
 */

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_su/common/show.dart';
import 'package:flutter_su/config/app_config.dart';
import 'package:flutter_su/config/request_config.dart';

import 'fun.dart';

// 统一处理http错误
class RequestError {
  final DioError error;
  final String msg;

  RequestError({@required this.error, this.msg});

  // 处理错误
  void httpError() {
    _error();
    _show();
    _printError();
  }

  /// 打印异常
  void _printError() {
    Map errorMap = {
      'message': error.message ?? '',
      'path': error.request.path ?? '',
      'requestData': error.request.data ?? '',
    };
    // FunCommon.debugPrint(errorMap);
    _writeDb(errorMap);
    SFunCommon.debugPrint(errorMap, msg: msg ?? 'Http出现异常');
  }

  // 弹窗
  void _show() {
    SFunCommon.debugPrint("_show方法",msg: error.message);
    Show.error(msg: error.message);
  }

  // 写入数据库
  void _writeDb(Map errorMap) {
    if (AppSetting.IS_DEBUG) {
      Map<String, dynamic> data = {
        "content": errorMap,
        "remarks": "网络请求错误${error.type}",
        "addTime":
            FunCommon().timestampToDate(DateTime.now().microsecondsSinceEpoch),
      };
      // TODO 保存到本地数据库
//      OcTestDb().insert(data);
    }
  }

  // 处理异常
  void _error() {
    switch (error.type) {
      case DioErrorType.CANCEL:
        SFunCommon.debugPrint("请求被取消", msg: "DioErrorType.CANCEL");
        break;
      case DioErrorType.CONNECT_TIMEOUT:
        SFunCommon.debugPrint("请求超时", msg: "DioErrorType.CONNECT_TIMEOUT");
        break;
      case DioErrorType.DEFAULT:
        SFunCommon.debugPrint("默认错误", msg: "${error.message}");
        break;
      case DioErrorType.RECEIVE_TIMEOUT:
        SFunCommon.debugPrint("接收超时", msg: "DioErrorType.RECEIVE_TIMEOUT");
        break;
      case DioErrorType.RESPONSE:
        SFunCommon.debugPrint("服务器抛出异常", msg: "DioErrorType.RESPONSE");
        _errorCode(error.response.statusCode);
        break;
      case DioErrorType.SEND_TIMEOUT:
        SFunCommon.debugPrint("网址发送超时时发生", msg: "DioErrorType.SEND_TIMEOUT");
        break;
      default:
      // FunCommon.debugPrint("默认错误", msg: "${error.message}");
    }
  }

  // 服务器错误码
  void _errorCode(int code) {
    switch (code) {
      case 401:
        break;
      case 503:
        // 服务器异常
//        EasyLoading.showError("服务器升级中，请稍等!");
        break;
      default:
    }
  }
}

/// 封装dio基类
class _BaseDio {
  Function _successCallback; // 成功回调
  Function _unSuccessCallBack; // 没有成功回调
  Function _errorCallback; // 异常回调

  // 返回请求是否成功
  DioResponseModel _getResponseStatus(Response response) {
    var data = response.data;
    if (!(data[RequestConfig.CODE_KEY] == RequestConfig.SUCCESS_CODE)) {
      // 请求不成功成功
      _unSuccessFun(response);
      String resultDesc = data['resultDesc'];
      Show.error(msg: resultDesc);
      return DioResponseModel(
          response: response,
          success: SuccessType.unSuccess,
          toJson: null,
          dioError: RequestErrorType.noError);
    }

    _successFun();
    return DioResponseModel(
      response: response,
      toJson: response.data,
      success: SuccessType.success,
      dioError: RequestErrorType.noError,
    );
  }

  // 踩坑，暂时不知道为什么要转换
  Map<String, dynamic> newMap(Map headers) {
    if (headers == null) {
      Map<String, dynamic> nullMap = {};
      return nullMap;
    }
    return Map<String, dynamic>.from(headers);
  }

  // 如果success回调函数不为空，这调用
  void _successFun() {
    if (_successCallback != null) {
      _successCallback();
    }
  }

  // 如果error回调函数不为空，这调用
  void errorFun() {
    // SuDialog.showToast('请求异常');
    if (_errorCallback != null) {
      _errorCallback();
    }
  }

  // 如过unSuccess回调函数不为空，则调用
  void _unSuccessFun(Response response) {
    Map map = {
      'path': response.request.path,
      'param': response.request.data,
      'status': response.statusCode,
      'data': response.data,
    };
    SFunCommon.debugPrint(map, msg: "打印错误(resultCode!=10000)");
    if (_unSuccessCallBack != null) {
      _unSuccessCallBack();
    }
  }
}

// 封装dio请求类
class Request with _BaseDio {

  final CancelToken cancel;
  Request({this.cancel});

  Dio _dio = Dio(); // 实例一个dio

//  Function _successCallback; // 成功回调
//  Function _unSuccessCallBack; // 没有成功回调
//  Function _errorCallback; // 异常回调

   Future<DioResponseModel> get(
    String path, {
    Map getData,
    Map postData,
    HttpMethod httpMethod,
    int timeOut,
    Map headersMap,
    Options options,
    Function successCallback,
    Function errorCallback,
    Function unSuccessCallBack,
  }) {
    return http(
      path,
      getData: getData,
      postData: postData,
      httpMethod: HttpMethod.get,
      timeOut: timeOut,
      headersMap: headersMap,
      options: options,
      successCallback: successCallback,
      errorCallback: errorCallback,
      unSuccessCallBack: unSuccessCallBack,
    );
  }

  Future<DioResponseModel> post(
    String path, {
    Map getData,
    Map postData,
    HttpMethod httpMethod,
    int timeOut,
    Map headersMap,
    Options options,
    Function successCallback,
    Function errorCallback,
    Function unSuccessCallBack,
  }) {
    return http(
      path,
      getData: getData,
      postData: postData,
      httpMethod: HttpMethod.post,
      timeOut: timeOut,
      headersMap: headersMap,
      options: options,
      successCallback: successCallback,
      errorCallback: errorCallback,
      unSuccessCallBack: unSuccessCallBack,
    );
  }

  /*
   * 发送http请求
   * @path String 请求地址(完整地址)
   * @queryParam Map get请求参数
   * @headersMap Map 请求头
   * @timeOut int 请求超时
   * @successCallback Function 成功回调
   * @unSuccessCallBack Function 失败回调
   * @errorCallback Function 异常回调
   * @options Options
   */
  Future<DioResponseModel> http(String path,
      {Map getData,
      Map postData,
      HttpMethod httpMethod,
      int timeOut,
      Map headersMap,
      Options options,
      Function successCallback,
      Function errorCallback,
      Function unSuccessCallBack}) async {
    _successCallback = successCallback ?? null;
    _errorCallback = errorCallback ?? null;
    _unSuccessCallBack = unSuccessCallBack ?? null;

    /// 请求头必须是Map<String, dynamic>类型
    Map<String, dynamic> _headers = headersMap ?? newMap(headersMap);
    // 设置超时
    _dio.options.connectTimeout = timeOut ?? RequestConfig.TIME_OUT;
    Options _options =
        options ?? Options(headers: _headers);
    Map _getData = getData == null ? null : newMap(getData);

    Response response;

    try {
      switch (httpMethod) {
        case HttpMethod.get:
          response = await _dio.get(path,
              queryParameters: _getData, options: _options);
          break;
        case HttpMethod.post:
          response = await _dio.post(path,
              queryParameters: _getData, options: _options, data: postData, cancelToken: cancel);
          break;
        case HttpMethod.delete:
          response = await _dio.delete(path,
              queryParameters: _getData, options: _options, data: postData);
          break;
        case HttpMethod.patch:
          response = await _dio.patch(path,
              queryParameters: _getData, options: _options, data: postData);
          break;
        case HttpMethod.put:
          response = await _dio.put(path,
              queryParameters: _getData, options: _options, data: postData);
          break;
      }
    } on DioError catch (error) {
      // 出现了dio异常
      RequestError(error: error, msg: "发起请求异常").httpError();
      return DioResponseModel(
        response: null,
        success: SuccessType.unSuccess,
        toJson: null,
        dioError: RequestErrorType.dioError,
      );
    } catch (e) {
      SFunCommon.debugPrint(e, msg: '其他的异常');
      return DioResponseModel(
        response: null,
        success: SuccessType.unSuccess,
        toJson: null,
        dioError: RequestErrorType.error,
      );
    }

    DioResponseModel dioModel = _getResponseStatus(response);
    return dioModel;
  }

  /*
   * 发送FormData请求
   * @path String 请求地址(完整地址)
   * @queryParam Map get请求参数
   * @data Map post请求参数
   * @headersMap Map 请求头
   * @timeOut int 请求超时
   * @successCallback Function 成功回调
   * @unSuccessCallBack Function 失败回调
   * @errorCallback Function 异常回调
   * @options Options
   * @showSchedule 是否显示进度
   */
  Future<DioResponseModel> formDataRequest(
    String path, {
    Map formDataMap,
    int timeOut,
    Map headersMap,
    Options options,
    Function successCallback,
    Function errorCallback,
    Function unSuccessCallBack,
    bool showSchedule = false,
    String showAddText = '',
  }) async {
    _successCallback = successCallback ?? null;
    _errorCallback = errorCallback ?? null;
    _unSuccessCallBack = unSuccessCallBack ?? null;

    Options _options = options ??
        Options(
          headers: headersMap ?? newMap(headersMap),
          sendTimeout: timeOut ?? 5000,
        );
    FormData formData = FormData.fromMap(newMap(formDataMap));

    Response response;

    try {
      response = await _dio.post(path,
          data: formData,
          options: _options, onSendProgress: (int count, int total) {
        var re = (count / total).toStringAsFixed(2);
        double data = double.parse(re);
        int percentData = (data * 100).toInt();
        EasyLoading.showProgress((count / total).toDouble(),
            status: "$percentData% $showAddText");
      });
    } on DioError catch (error) {
      // 出现了dio异常
      RequestError(error: error, msg: "发起请求异常").httpError();
      return DioResponseModel(
        response: null,
        success: SuccessType.unSuccess,
        toJson: null,
        dioError: RequestErrorType.dioError,
      );
    } catch (e) {
      SFunCommon.debugPrint(e, msg: '其他的异常');
      return DioResponseModel(
        response: null,
        success: SuccessType.unSuccess,
        toJson: null,
        dioError: RequestErrorType.error,
      );
    }
    DioResponseModel dioModel = _getResponseStatus(response);
    return dioModel;
  }
}

// 成功枚举
enum SuccessType {
  success,
  unSuccess,
}

// http

enum HttpMethod {
  get,
  delete,
  patch,
  post,
  put,
}

enum RequestErrorType {
  dioError,
  error,
  noError,
}

// 封装统一返回对象
class DioResponseModel {
  Response response;
  dynamic toJson;
  SuccessType success;
  RequestErrorType dioError;

  DioResponseModel({
    @required this.response,
    @required this.toJson,
    @required this.success,
    this.dioError,
  });
}
